#pragma once
#include <boost/signals.hpp>
#include "Point.h"
#include "Rect.h"
#include "TimerType.h"
#include "CursorType.hpp"

namespace ja2{ namespace sgp
{
	//! Mouse region.
	class MouseRegion : boost::noncopyable
	{
		// Enums
	public:
		//! Priority
		enum Priority
		{
			MSYS_PRIORITY_LOWEST = 0,
			MSYS_PRIORITY_LOW = 15,
			MSYS_PRIORITY_BASE = 31,
			MSYS_PRIORITY_NORMAL = MSYS_PRIORITY_BASE,
			MSYS_PRIORITY_HIGH = 63,
			MSYS_PRIORITY_HIGHEST = 127,
			MSYS_PRIORITY_SYSTEM = -1,
			MSYS_PRIORITY_AUTO = MSYS_PRIORITY_SYSTEM
		};
		//! Flag.
		enum Flag
		{
			F_MSYS_NO_FLAGS = 0x00000000,
			F_MSYS_MOUSE_IN_AREA = 0x00000001,
			F_MSYS_SET_CURSOR = 0x00000002,
			F_MSYS_MOVE_CALLBACK = 0x00000004,
			F_MSYS_BUTTON_CALLBACK = 0x00000008,
			F_MSYS_REGION_EXISTS = 0x00000010,
			F_MSYS_SYSTEM_INIT = 0x00000020,
			F_MSYS_REGION_ENABLED = 0x00000040,
			F_MSYS_FASTHELP = 0x00000080,
			F_MSYS_GOT_BACKGROUND = 0x00000100,
			F_MSYS_HAS_BACKRECT = 0x00000200,
			F_MSYS_FASTHELP_RESET = 0x00000400,
			F_MSYS_ALLOW_DISABLED_FASTHELP = 0x00000800
		};
		//! Mouse system callback reasons.
		enum CallbackReason
		{
			CR_MSYS_CALLBACK_REASON_NONE = 0,
			CR_MSYS_CALLBACK_REASON_INIT = 1,
			CR_MSYS_CALLBACK_REASON_MOVE = 2,
			CR_MSYS_CALLBACK_REASON_LBUTTON_DWN = 4,
			CR_MSYS_CALLBACK_REASON_LBUTTON_UP = 8,
			CR_MSYS_CALLBACK_REASON_RBUTTON_DWN = 16,
			CR_MSYS_CALLBACK_REASON_RBUTTON_UP = 32,
			CR_MSYS_CALLBACK_REASON_LOST_MOUSE = 64,
			CR_MSYS_CALLBACK_REASON_GAIN_MOUSE = 128,
			CR_MSYS_CALLBACK_REASON_LBUTTON_REPEAT = 256,
			CR_MSYS_CALLBACK_REASON_RBUTTON_REPEAT = 512,
			// Added support for double clicks.	The DOUBLECLICK event is passed combined with
			// the LBUTTON_DWN event if two LBUTTON_DWN events are detected on the same button/region
			// within the delay defined by MSYS_DOUBLECLICK_DELAY (in milliseconds).	If your button/region
			// supports double clicks and single clicks, make sure the DOUBLECLICK event is checked first (rejecting
			// the LBUTTON_DWN event if detected)
			CR_MSYS_CALLBACK_REASON_LBUTTON_DOUBLECLICK = 1024,
			CR_MSYS_CALLBACK_REASON_MBUTTON_DWN = 2048,
			CR_MSYS_CALLBACK_REASON_MBUTTON_UP = 4096,
			CR_MSYS_CALLBACK_REASON_MBUTTON_REPEAT = 8192,
			CR_MSYS_CALLBACK_REASON_WHEEL_UP = 16384,
			CR_MSYS_CALLBACK_REASON_WHEEL_DOWN = 32768,
			CR_MSYS_CALLBACK_REASON_X1BUTTON_DWN = 65536,
			CR_MSYS_CALLBACK_REASON_X1BUTTON_UP = 131072,
			CR_MSYS_CALLBACK_REASON_X1BUTTON_REPEAT = 262144,
			CR_MSYS_CALLBACK_REASON_X2BUTTON_DWN = 524288,
			CR_MSYS_CALLBACK_REASON_X2BUTTON_UP = 1048576,
			CR_MSYS_CALLBACK_REASON_X2BUTTON_REPEAT = 2097152,
			CR_MSYS_CALLBACK_REASON_BUTTONS = (CR_MSYS_CALLBACK_REASON_LBUTTON_DWN | CR_MSYS_CALLBACK_REASON_LBUTTON_UP | \
												CR_MSYS_CALLBACK_REASON_RBUTTON_DWN | CR_MSYS_CALLBACK_REASON_RBUTTON_UP | \
												CR_MSYS_CALLBACK_REASON_MBUTTON_DWN | CR_MSYS_CALLBACK_REASON_MBUTTON_UP |\
												CR_MSYS_CALLBACK_REASON_X1BUTTON_DWN | CR_MSYS_CALLBACK_REASON_X1BUTTON_UP |\
												CR_MSYS_CALLBACK_REASON_X2BUTTON_DWN | CR_MSYS_CALLBACK_REASON_X2BUTTON_UP),
			CR_MSYS_CALLBACK_REASON_WHEEL = (CR_MSYS_CALLBACK_REASON_WHEEL_UP | CR_MSYS_CALLBACK_REASON_WHEEL_DOWN)
		};
		//! Mouse system button masks.
		enum ButtonState
		{
			BS_MSYS_LEFT_BUTTON = 1,
			BS_MSYS_RIGHT_BUTTON = 2,
			BS_MSYS_MIDDLE_BUTTON = 4,
			BS_MSYS_X1_BUTTON = 8,
			BS_MSYS_X2_BUTTON = 16
		};

		// Typedefs
	public:
		//! Signal type for callback. Second parameter is callback reason
		// combination.
		typedef boost::signal<void (MouseRegion const &, std::uint_least32_t)> CallbackSignal_t;

		// Constants
	public:
		//! Base region flags.
		static std::uint_least32_t const BASE_REGION_FLAGS = (F_MSYS_REGION_ENABLED | F_MSYS_SET_CURSOR);

		// Attributes
	private:
		//! Region's Priority, set by system and/or caller.
		Priority m_PriorityLevel;
		//! Region's state flags
		/*!
			--REFERENCE--: uiFlags
		*/
		std::uint_least32_t m_Flags;
		//! Screen area affected by this region (absolute coordinates).
		/*!
			We probably can use Rect here but rect coordinates are unsigned.

			--REFERENCE--: 	RegionTopLeftX RegionTopLeftY RegionBottomRightX RegionBottomRightY
		*/
		std::int_least16_t m_TopLeftX;
		std::int_least16_t m_TopLeftY;
		std::int_least16_t m_BottomRightX;
		std::int_least16_t m_BottomRightY;
		//! Mouse's Coordinates in absolute screen coordinates.
		Point m_MousePosition;
		//! Mouse's Coordinates relative to the Top-Left corner of the region.
		Point m_MousePositionRelative;
		//! Current state of the mouse buttons.
		std::uint_least16_t m_ButtonState;
		//! Cursor to use when mouse in this region (see flags).
		/*!
			--REFERENCE--: Cursor
		*/
		boost::optional<CursorType> m_Cursor;
		//! Signal if movement occurs in this region.
		CallbackSignal_t m_MovementCallback;
		//! Signal if button action occurs in this region.
		CallbackSignal_t m_ButtonCallback;
//x		INT32									UserData[4];				// User Data, can be set to anything!
		//Fast help vars.
		//! Countdown timer for FastHelp text.
		/*!
			--REFERENCE--: FastHelpTimer
		*/
		std::uint_least16_t m_FastHelpTimer;
		//! Text string for the FastHelp (describes buttons if left there a while).
		/*!
			--REFERENCE--: FastHelpText
		*/
		std::string m_FastHelpText;
//x		INT32					FastHelpRect;
//x		MOUSE_HELPTEXT_DONE_CALLBACK HelpDoneCallback;
//x		struct _MOUSE_REGION	*next;							// List maintenance, do NOT touch these entries
//x		struct _MOUSE_REGION	*prev;

		// Operations
	public:
		//! Get priority.
		Priority priority() const;
		//! Set mouse movement callback.
		void move_signal(CallbackSignal_t::slot_type const &Signal);
		//! Set button callback.
		void button_signal(CallbackSignal_t::slot_type const &Signal);
		//! Is flag set.
		bool is_flag_set(Flag Flag_) const;
		//! Unset flag.
		/*!
			--REFERENCE--: this->uiFlags &= (~XXX);
		*/
		void unset_flag(Flag Flag_);
		//! Set flag.
		void set_flag(Flag Flag_);
		//! Find if the passed coordinate is in region.
		bool is_inside(std::int_least16_t TopLeftX, std::int_least16_t TopLeftY, std::int_least16_t BottomRightX, std::int_least16_t BottomRightY) const;
		//! Get fast help text.
		std::string const &fast_help_text() const;
		//! Set fast help timer.
		void fast_help_timer(std::uint_least16_t Value);
		//! Call movement callback.
		void movement_callback(std::uint_least32_t Reason) const;
		//! Is cursor set.
		bool is_cursor_set() const;
		//! Set cursor.
		void set_cursor(CursorType Cursor);
		//! Get cursor.
		CursorType cursor() const;
		//! Set mouse position.
		void set_mouse_position(Point const &Pos);
		//! Get region top left x.
		std::int_least16_t const &region_top_left_x() const;
		//! Get region top left y.
		std::int_least16_t const &region_top_left_y() const;
		//! Get region bottom right x.
		std::int_least16_t const &region_bottom_right_x() const;
		//! Get region bottom right y.
		std::int_least16_t const &region_bottom_right_y() const;
		//! Set button state.
		void set_button_state(ButtonState State);
		//! Set whole button state.
		void set_button_state_all(std::uint_least16_t State);
		//! Button callback call.
		void button_callback(std::uint_least32_t Reason);

		// Construction/Destruction
	public:
		MouseRegion(Priority Priority_, std::uint_least32_t Flags, std::int_least16_t LeftX, std::int_least16_t LeftY, std::int_least16_t RightX, std::int_least16_t RightY, Point const &MousePos, Point const &MousePosRel, std::uint_least16_t ButtonState, std::uint_least16_t FastHelptimer, std::string const &FastHelpText);
	};

	inline std::int_least16_t const &MouseRegion::region_top_left_x() const
	{
		return m_TopLeftX;
	}

	inline std::int_least16_t const &MouseRegion::region_top_left_y() const
	{
		return m_TopLeftY;
	}

	inline std::int_least16_t const &MouseRegion::region_bottom_right_x() const
	{
		return m_BottomRightX;
	}

	inline std::int_least16_t const &MouseRegion::region_bottom_right_y() const
	{
		return m_BottomRightY;
	}
}/*sgp*/} /*ja2*/
